package com.jblog.controller.front.action.account;

import static com.jblog.Constants.System.ACCOUNT_ID;
import static com.jblog.Constants.System.OK;
import static com.jblog.Constants.System.PASSWORD_RESET_CODE;
import static com.jblog.Constants.System.TIPS;
import static com.jblog.Constants.Template.Mail.ACCOUNT_ACTIVE;
import static com.jblog.Constants.Template.Mail.ACCOUNT_PASSWORD_RESET;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.jblog.ErrorCode;
import com.jblog.common.util.MD5;
import com.jblog.common.util.ValidateUtil;
import com.jblog.common.web.CookieUtils;
import com.jblog.controller.front.action.BaseAct;
import com.jblog.controller.web.SiteUtils;
import com.jblog.core.model.account.Account;
import com.jblog.core.model.common.Role;
import com.jblog.core.model.common.Status;
import com.jblog.core.model.mail.MailInfo;

@Controller
@RequestMapping("/account")
public class AccountAct extends BaseAct {
    
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login(HttpServletRequest request, ModelMap model) {
        return F(request, model, "account/login");
    }
    
    public boolean isLogin(HttpServletRequest request) {
        return null == SiteUtils.getAccount(request);
    }
    
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public @ResponseBody String login(HttpServletRequest request,
            HttpServletResponse response, ModelMap model, Account account) {
        String msg = accountValidation(request, account, false);
        if (!OK.equals(msg)) {
            return msg;
        }
        Account ac = queryAccountByName(account.getName());
        if (null == ac) {
            return ErrorCode.Account.ACCOUNT_INCORRECT;
        } else if (!ac.getPass().equals(MD5.twice(account.getPass()))) {
            return ErrorCode.Account.ACCOUNT_INCORRECT;
        } else if (Status.INVALID == ac.getStatus()) {
            return ErrorCode.Account.ACCOUNT_INVALID;
        } else if (Status.CANCEL == ac.getStatus()){
            return ErrorCode.Account.ACTIVATE_LOCKED;
        } 
        
        if (account.getIsCookie()) {
            CookieUtils.writeCookie(request, response, ACCOUNT_ID, 
                ac.getId().toString(), com.jblog.Constants.Cookie.ACCOUNT_AGE);
        }
        request.getSession().setAttribute(ACCOUNT_ID, ac.getId());
        return OK;
    }
    
    @RequestMapping(value={"/profile"}, method={org.springframework.web.bind.annotation.RequestMethod.POST})
    public String profile(HttpServletRequest request, HttpServletResponse response, ModelMap model) {
        if (null == SiteUtils.getAccount(request)) {
            return O(request, model, "main/dialog/403");
        }
        return F(request, model, "account/profile");
    }

    @RequestMapping(value = { "/update" }, method = { org.springframework.web.bind.annotation.RequestMethod.POST })
	public @ResponseBody String update(HttpServletRequest request,
			HttpServletResponse response, ModelMap model, Account account) {
		String msg = updateValidation(request, account);
		if (!OK.equals(msg))
			return msg;

		Account request_ac = SiteUtils.getAccount(request);
		Account temp_ac = new Account();

		boolean flag = false;
		if (!(account.getPhoto().equals(request_ac.getPhoto()))) {
			temp_ac.setPhoto(account.getPhoto());
			flag = true;
		}
		if (!(account.getNickName().equals(request_ac.getNickName()))) {
			temp_ac.setNickName(account.getNickName());
			flag = true;
		}
		if (flag) {
			temp_ac.setId(request_ac.getId());
			this.iAccount.updateAccount(temp_ac);
		}

		return OK;
	}
    
    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    public String logout(HttpServletRequest request, HttpServletResponse response) {
        CookieUtils.removeCookie(request, response, ACCOUNT_ID);
        request.getSession().removeAttribute(ACCOUNT_ID);
        return "redirect:/";
    }
    
    @RequestMapping(value = "/register", method = RequestMethod.GET)
    public String register(HttpServletRequest request, ModelMap model) {
        return F(request, model, "account/register");
    }

    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public @ResponseBody String register(HttpServletRequest request, ModelMap model, Account account) {
        String msg = accountValidation(request, account, true);
        if (!OK.equals(msg)) {
            return msg;
        }
        account.setPass(MD5.twice(account.getPass()));
        account.setStatus(Status.INVALID);
        account.setRole(Role.GUEST);
        account.setActiveCode(toActivateHex(account));
        account.setLastLoginTime(account.getLastUpdTime());
        
        iAccount.saveAccount(account);
        sendActivateEmailToRegisterAccount(request, account);
        return OK;
    }
    
    @RequestMapping(value = "/activate", method = RequestMethod.GET)
    public String activate(HttpServletRequest request, ModelMap model) {
        return F(request, model, "account/activate");
    }
    @RequestMapping(value = "/activate", method = RequestMethod.POST)
    public @ResponseBody String activate(HttpServletRequest request, ModelMap model, Account account) {
        
        String msg = accountValidation2(request, account, true);
        if (!OK.equals(msg)) {
            return msg;
        }
        if (Status.NORMAL == account.getStatus()) {
            return ErrorCode.Account.ACCOUNT_ACTIVATED_AREADY;
        }
        
        account.setActiveCode(toActivateHex(account));
        sendActivateEmailToRegisterAccount(request, account);
        
        return OK;
    }
    
    @RequestMapping(value = "/activate/{activeCode}", method = RequestMethod.GET)
    public String activate(HttpServletRequest request, ModelMap model, @PathVariable String activeCode) {
        Account ac = iAccount.findAccountByActiveCode(activeCode);
        boolean result = false;
        if (null != ac) {
            if (ac.getStatus() != Status.NORMAL) {
                Account account = new Account();
                account.setId(ac.getId());
                account.setStatus(Status.NORMAL);
                iAccount.updateAccount(account);
            }
            result = true;
            model.put("account", ac);
        }
        if (!result) {
            model.put(TIPS, "Invalid activate code");
        }
        model.put("result", result);
        return F(request, model, "account/activate_result");
    }
    
    @RequestMapping(value = "/password/reset/request", method = RequestMethod.GET)
    public String passwordResetRequest(HttpServletRequest request, ModelMap model) {
        return F(request, model, "account/password_reset_request");
    }
    
    @RequestMapping(value = "/password/reset/request", method = RequestMethod.POST)
    public @ResponseBody String passwordResetRequest(HttpServletRequest request, ModelMap model, Account account) {
        String msg = accountValidation2(request, account, true);
        if (!OK.equals(msg)) {
            return msg;
        }
        
        account.setActiveCode(toActivateHex(account));
        sendPasswordResetEmailToAccount(request, account);
        
        request.getSession().setAttribute(PASSWORD_RESET_CODE, account.getActiveCode());
        return OK;
    }
    
    @RequestMapping(value = "/password/reset/{email}/{name}/{activeCode}", method = RequestMethod.GET)
    public String passwordReset(HttpServletRequest request, ModelMap model,
            @PathVariable String email, @PathVariable String name,
            @PathVariable String activeCode) {
        Account account = new Account();
        account.setEmail(email);
        account.setName(name);
        account.setActiveCode(activeCode);
        
        model.put("account", account);
        
        return F(request, model, "account/password_reset");
    }
    @RequestMapping(value = "/password/reset", method = RequestMethod.POST)
    public @ResponseBody String passwordReset(HttpServletRequest request, ModelMap model, Account account) {
        String msg = accountValidation2(request, account, false);
        if (!OK.equals(msg)) {
            return msg;
        }
        
        String code = (String)request.getSession().getAttribute(PASSWORD_RESET_CODE);
        if (StringUtils.isBlank(code)) {
            return ErrorCode.Account.ACTIVATE_CODE_INVALID;
        } else {
            request.getSession().removeAttribute(PASSWORD_RESET_CODE);
        }
        
        if (StringUtils.isNotBlank(account.getPass())) {
            Account ac = new Account();
            ac.setId(account.getId());
            ac.setPass(MD5.twice(account.getPass()));
            
            iAccount.updateAccount(ac);
        }
        return OK;
    }
    
    @RequestMapping(value= "/password/change", method = RequestMethod.POST)
    @ResponseBody
    public String passwordChange(HttpServletRequest request, ModelMap model, Account account) {
      String msg = accountValidation3(request, account);
      if (!OK.equals(msg))
        return msg;

      Account request_ac = SiteUtils.getAccount(request);
      Account temp_ac = new Account();
      temp_ac.setId(request_ac.getId());
      temp_ac.setPass(MD5.twice(account.getPass()));

      this.iAccount.updateAccount(temp_ac);

      return OK;
    }
    
    private Account queryAccountByName(String name) {
        return iAccount.findAccountByName(name);
    }
    
    private String toActivateHex(Account account) {
        return MD5.twice(account.getName() + account.getPass() + account.getEmail());
    }
    
    private void sendActivateEmailToRegisterAccount(HttpServletRequest request, Account account) {
        MailInfo mail = new MailInfo();
        mail.setTemplate(ACCOUNT_ACTIVE);
        mail.setData(account);
        mail.setSite(isite.findSite());
        mail.setTo(account.getEmail());
        mail.setSubject("Get Started with Jblog");
        imail.sendMail(mail);
    }
    private void sendPasswordResetEmailToAccount(HttpServletRequest request, Account account) {
        MailInfo mail = new MailInfo();
        mail.setTemplate(ACCOUNT_PASSWORD_RESET);
        mail.setData(account);
        mail.setSite(isite.findSite());
        mail.setTo(account.getEmail());
        mail.setSubject("account password reset - Jblog");
        imail.sendMail(mail);
    }
    
    private String accountValidation(HttpServletRequest request,
            Account account, boolean b) {
        String result;
        //validate of nickName
        if (b) {
            result = ValidateUtil.helperRegex(account.getNickName(), "\\w{2,15}",
                    ErrorCode.Account.NICK_NAME_ERROR);
            if (!OK.equals(result)) return result;
        }
        
        //validate of email format
        if (b) {
            result = ValidateUtil.isEmail(account.getEmail());
            if (!OK.equals(result)) return result;
        }
        
        //validate of account name
        result = ValidateUtil.helperRegex(account.getName(), "\\w{2,15}",
                ErrorCode.Account.AC_NAME_ERROR);
        if (!OK.equals(result)) return result;
        
        //validate of account password
        result = ValidateUtil.helperRegex(account.getPass(), "\\w{6,18}",
                ErrorCode.Account.PASSWORD_ERROR);
        if (!OK.equals(result)) return result;
        
        //validate of photo
        if (b) {
            result = ValidateUtil.helperEquals(account.getPhoto(), "-1",
                    ErrorCode.Account.PHOTO_ERROR);
            if (!OK.equals(result)) return result;
        }
        
        String captcha = ValidateUtil.captcha(request, account.getCaptcha());
        if (!OK.equals(captcha)) {
            return captcha;
        }
        
        //check unique email and accountName
        if (b) {
            Account ac = iAccount.findAccountByEmail(account.getEmail());
            if (null != ac) {
                return ErrorCode.Account.EMAIL_EXIST;
            }
        
            ac = queryAccountByName(account.getName());
            if (null != ac) {
                return ErrorCode.Account.AC_NAME_EXIST;
            }
        }
        return OK;
    }
    
    private String accountValidation2(HttpServletRequest request,
            Account account, boolean b) {
        String result;
        
        if (b) {
            //validate of email format
            result = ValidateUtil.isEmail(account.getEmail());
            if (!OK.equals(result)) return result;
    
            //validate of account name
            result = ValidateUtil.helperRegex(account.getName(), "\\w{2,15}",
                    ErrorCode.Account.AC_NAME_ERROR);
            if (!OK.equals(result)) return result;
        } else {
            //validate of account password
            result = ValidateUtil.helperRegex(account.getPass(), "\\w{6,18}",
                    ErrorCode.Account.PASSWORD_ERROR);
            if (!OK.equals(result)) return result;
        }
        
        String captcha = ValidateUtil.captcha(request, account.getCaptcha());
        if (!OK.equals(captcha)) {
            return captcha;
        }
        
        //check email and accountName
        Account emailAc = iAccount.findAccountByEmail(account.getEmail());
        if (null == emailAc) {
            return ErrorCode.Account.EMAIL_NOT_EXIST;
        }
    
        Account nameAc = queryAccountByName(account.getName());
        if (null == nameAc) {
            return ErrorCode.Account.AC_NAME_NOT_EXIST;
        } else if (!emailAc.getEmail().equals(nameAc.getEmail())) {
            return ErrorCode.Account.ACCOUNT_OR_EMAIL_ERROR;
        } else if (Status.CANCEL == nameAc.getStatus()){
            return ErrorCode.Account.ACTIVATE_LOCKED;
        } else {
            account.setId(nameAc.getId());
            account.setNickName(nameAc.getNickName());
            if (StringUtils.isBlank(account.getPass())) {
                account.setPass(nameAc.getPass());
            } else if (MD5.twice(account.getPass()).equals(nameAc.getPass())){
                account.setPass(null);
            }
            account.setStatus(nameAc.getStatus());
        }
        
        return OK;
    }
    
    private String accountValidation3(HttpServletRequest request, Account account) {
      String result = ValidateUtil.helperRegex(account.getOldPass(), 
    		  "\\w{6,18}", ErrorCode.Account.OLD_PASSWORD_ERROR);

      if (!OK.equals(result)) { 
    	  return result;
      }

      result = ValidateUtil.helperRegex(account.getPass(), 
    		  "\\w{6,18}", ErrorCode.Account.NEW_PASSWORD_ERROR);

      if (!OK.equals(result)) {
    	  return result;
      }

      if (account.getPass().equals(account.getOldPass())) {
    	  return ErrorCode.Account.OLD_NEW_PASSWORD_ERROR;
      }

      Account ac = SiteUtils.getAccount(request);
      if (null == ac)
        return ErrorCode.Common.E403;
      if (!(ac.getPass().equals(MD5.twice(account.getOldPass())))) {
        return ErrorCode.Account.OLD_PASSWORD_INCORRECT;
      }

      return OK;
    }
    
	private String updateValidation(HttpServletRequest request, Account account) {
		String result = ValidateUtil.helperRegex(account.getNickName(), 
				"\\w{2,15}", ErrorCode.Account.NICK_NAME_ERROR);
		if (!OK.equals(result)) {
			return result;
		}
		
		result = ValidateUtil.helperEquals(account.getPhoto(), "-1", 
				ErrorCode.Account.PHOTO_ERROR);
		if (!OK.equals(result)) {
			return result;
		}
		if (!isLogin(request)) {
			return ErrorCode.Common.E403;
		}
		return OK;
	}

}